import { Injectable, Inject } from "@angular/core";
import { PLATFORM_ID, APP_ID } from "@angular/core";
import { isPlatformBrowser, isPlatformServer } from "@angular/common";
import {
  HttpClient,
  HttpHeaders,
  HttpErrorResponse,
  HttpEventType,
  HttpEvent,
  HttpParams,
  HttpRequest
} from "@angular/common/http";
import { Observable, of, throwError } from "rxjs";
import { catchError, tap, map, last, filter } from "rxjs/operators";

const httpOptions = {
  headers: new HttpHeaders({ "Content-Type": "application/json" })
};
const apiUrl = "/File";

@Injectable({
  providedIn: "root"
})
export class FileService {
  isServer: boolean;

  cos: Cos = new Cos(this);

  s3: S3 = new S3(this);

  constructor(
    public http: HttpClient,
    @Inject(PLATFORM_ID) private platformId: Object
  ) {
    this.isServer = isPlatformServer(platformId);
  }

  private extractData(res: Response) {
    const body = res;
    return body || {};
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error("An error occurred:", error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` + `body was: ${error.error}`
      );
    }
    // return an observable with a user-facing error message
    return throwError("Something bad happened; please try again later.");
  }
}

class Cos {
  cos: any = null;

  Bucket = "shop-1251666674";
  Region = "ap-beijing-1";

  constructor(private fileService: FileService, private http?: HttpClient) {
    if (this.fileService.isServer) {
      this.init();
    }
  }

  init() {
    this.cos = this.createCos();
  }

  createCos() {
    const COS = require("../../assets/lib/cos");
    return new COS({
      Bucket: this.Bucket,
      Region: this.Region,
      getAuthorization: async (options, callback) => {
        const url = `${apiUrl}/auth`;
        const res: any = await this.http
          .get(url, httpOptions)
          .pipe(
            map(this.extractData),
            catchError(this.handleError)
          )
          .toPromise();
        if ("200" === res.code) {
          callback({
            TmpSecretId: res.data.credentials.tmpSecretId,
            TmpSecretKey: res.data.credentials.tmpSecretKey,
            XCosSecurityToken: res.data.credentials.sessionToken,
            ExpiredTime: res.data.expiredTime
          });
        }
      }
    });
  }

  add(params) {
    return new Promise((resolve, reject) => {
      this.cos.sliceUploadFile(
        {
          Bucket: this.Bucket /* 必须 */,
          Region: this.Region /* 必须 */,
          Key: params.name /* 必须 */,
          Body: params.file /* 必须 */,
          TaskReady: function(taskId) {
            /* 非必须 */
            console.log(taskId);
          },
          onHashProgress: function(progressData) {
            /* 非必须 */
            console.log(JSON.stringify(progressData));
          },
          onProgress: function(progressData) {
            /* 非必须 */
            console.log(JSON.stringify(progressData));
          }
        },
        function(err, data) {
          if (err) {
            reject(err);
          } else {
            resolve(data);
          }
        }
      );
    }).then((data: any) => {
      return {
        bucket: data.Bucket,
        name: data.Key,
        url: data.Location
      };
    });
  }

  private extractData(res: Response) {
    const body = res;
    return body || {};
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error("An error occurred:", error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` + `body was: ${error.error}`
      );
    }
    // return an observable with a user-facing error message
    return throwError("Something bad happened; please try again later.");
  }
}

class S3 {
  constructor(private fileService: FileService, private http?: HttpClient) {
    if (this.fileService.isServer) {
    }
  }

  private extractData(res: Response) {
    const body = res;
    return body || {};
  }

  // file from event.target.files[0]
  addFile(file: File): Observable<HttpEvent<any>> {
    const formData = new FormData();
    formData.append("file", file);

    const params = new HttpParams();

    const options = {
      params: params,
      reportProgress: true
    };

    const url = `${apiUrl}/`;
    const req = new HttpRequest("POST", url, formData, options);
    return this.fileService.http.request(req).pipe(
      filter((res: any) => !!res.body),
      map((res: any) => res.body),
      catchError(this.handleError)
    );
  }

  private handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error("An error occurred:", error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` + `body was: ${error.error}`
      );
    }
    // return an observable with a user-facing error message
    return throwError("Something bad happened; please try again later.");
  }

  private getEventMessage(event: HttpEvent<any>, file: File) {
    switch (event.type) {
      case HttpEventType.Sent:
        return `Uploading file "${file.name}" of size ${file.size}.`;

      case HttpEventType.UploadProgress:
        // Compute and show the % done:
        const percentDone = Math.round((100 * event.loaded) / event.total);
        return `File "${file.name}" is ${percentDone}% uploaded.`;

      case HttpEventType.Response:
        return `File "${file.name}" was completely uploaded!`;

      default:
        return `File "${file.name}" surprising upload event: ${event.type}.`;
    }
  }
  showProgress(msg: string) {}
}
